class_name ObjectPool
extends Node


## 对象池[br]
## 支持对象自定义激活/回收逻辑（通过 _on_reactivated 和 _on_released 方法）[br]
## 需要自动回收的对象应实现 destroyed 信号[br]
## 对于高频生成的对象（子弹、特效等），建议预加载数量设为预期同时存在的最大数量+20%[br]


@export var scene: PackedScene
@export var size: int

## 最大池容量，为0时表示无限制
@export var capacity: int

## 是否允许自动扩容
@export var auto_expand: bool = true

var _available_objs: Array[Node] = []
var _active_objs: Array[Node] = []


func _ready() -> void:
    init_pool()


func _create_new_obj() -> Node:
    var obj := scene.instantiate()
    add_child(obj)
    _deactivate_obj(obj)
    return obj


func _activate_obj(obj: Node) -> void:
    obj.show()
    obj.process_mode = Node.PROCESS_MODE_INHERIT
    if obj.has_method("_on_reactivated"):
        obj._on_reactivated()


func _deactivate_obj(obj : Node) -> void:
    obj.hide()
    obj.process_mode = Node.PROCESS_MODE_DISABLED
    if obj.has_method("_on_released"):
        obj._on_released()


func _is_below_max_capacity() -> bool:
    return capacity <= 0 or get_total_count() < capacity


func init_pool() -> void:
    if not scene:
        push_error("ObjectPool: Target scene not set!")
        return
    
    for i in range(size):
        var obj := _create_new_obj()
        _available_objs.append(obj)


func acquire() -> Node:
    if _available_objs.is_empty():
        if can_expand():
            var new_obj := _create_new_obj()
            _active_objs.append(new_obj)
            _activate_obj(new_obj)
            return new_obj
        else:
            push_warning("ObjectPool: Pool exhausted and cannot expand")
            return null
    
    var obj: Node = _available_objs.pop_front()
    _active_objs.append(obj)
    _activate_obj(obj)
    return obj


func release(obj: Node) -> void:
    if not _active_objs.has(obj):
        push_warning("ObjectPool: Trying to release unmanaged object")
        return
    
    _deactivate_obj(obj)
    _active_objs.erase(obj)
    if _is_below_max_capacity():
        _available_objs.append(obj)
        # print("Object Pool: release")
    else:
        obj.queue_free()
        # print("Object Pool: queue free")
    

func clear_pool() -> void:
    for obj in _available_objs + _available_objs:
        obj.queue_free()
    _available_objs.clear()
    _active_objs.clear()


func get_total_count() -> int:
    return _available_objs.size() + _active_objs.size()


func can_expand() -> bool:
    return auto_expand and (capacity <= 0 or get_total_count() < capacity)


static func setup_auto_release(obj: Node, pool: ObjectPool) -> void:
    if obj.has_signal("destroyed"):
        obj.destroyed.connect(pool.release.bind(obj))
    else:
        push_warning("ObjectPool: Auto-release requires 'destroyed' signal")


static func release_obj(obj: Node) -> void:
    var parent = obj.get_parent()
    if parent is ObjectPool:
        parent.release(obj)
    else:
        obj.queue_free()
